from flask_restful import Resource, marshal, reqparse, marshal_with
from flask import current_app, jsonify
import bcrypt

from flaskr.models.application import Application
from ..models import User, TokenBlocklist
from flask_jwt_extended import create_access_token, jwt_required, current_user, get_jwt
from ..auth import jwt
from .dto import user_profile_dto, application_dto, user_profile_with_missions_dto
from .util import remove_none_fields
from ..error import InvalidUsage

parser = reqparse.RequestParser()
parser.add_argument('username', type=str,
                    help='a unique string username')
parser.add_argument('password', type=str,
                    help='a string password')
parser.add_argument('oldPassword', type=str,
                    help='a string password')
parser.add_argument('phone', type=str)
parser.add_argument('description', type=str)
parser.add_argument('tags', action='append')


def crypt_password(password: str):
    salt = bcrypt.gensalt()
    hashed = bcrypt.hashpw(password.encode(), salt)
    return hashed


class UserRegisterService(Resource):
    def post(self):
        args = remove_none_fields(parser.parse_args())
        # current_app.logger.info(args)
        password = args['password']
        hashed = crypt_password(password)
        args['password'] = hashed
        user = User(**args)
        user.save()

        access_token = create_access_token(identity=user)
        return jsonify(accessToken=access_token)


class UserLogService(Resource):
    # login
    def post(self):
        args = remove_none_fields(parser.parse_args())
        username, password = args['username'], args['password']

        user = User.query_by_username(username=username)
        if user is None:
            raise InvalidUsage("User not found", 404)

        if not user.check_password(password):
            raise InvalidUsage("Wrong username or password", 401)

        current_app.logger.info("login, user id: {}".format(user.id))
        access_token = create_access_token(identity=user)

        return jsonify(accessToken=access_token)

    # logout
    @jwt_required()
    def delete(self):
        jti = get_jwt()["jti"]
        tb = TokenBlocklist(jti=jti)
        tb.save()
        return jsonify(msg="Logout")


class UserProfileService(Resource):
    # profile
    @jwt_required()
    @marshal_with(user_profile_with_missions_dto)
    def get(self):
        user = User.query_by_id(identify=current_user.id)
        if user is None:
            raise InvalidUsage("{}", 200)
        # current_app.logger.info("tags: {}".format(user.tags))
        return user

    # update profile
    @jwt_required()
    @marshal_with(user_profile_dto)
    def put(self):
        args = remove_none_fields(parser.parse_args())
        user = User.query_by_id(identify=current_user.id)
        if user is None:
            raise InvalidUsage("{}", 200)
        if 'password' in args.keys():
            old_password = args['oldPassword']
            if not user.check_password(old_password):
                raise InvalidUsage("old password wrong", 401)
            hashed = crypt_password(args['password'])
            args['password'] = hashed
            del args['oldPassword']
        user = User.update_by_id(current_user.id, args)
        return user


class UserApplicationService(Resource):
    @jwt_required()
    # @marshal_with(application_dto)
    def get(self, mission_id):
        ap = Application.query_by_mission_id_and_user_id(
            mission_id=mission_id, user_id=current_user.id)
        if ap is None:
            return {}
        return marshal(ap, application_dto)
